Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.

...powered by www.netzwerkartist.de...

 << zurück
Visual C# 2005 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2005

Visual C# 2005
1.320 S., mit 2 CDs, 59,90 Euro
Galileo Computing
ISBN 3-89842-586-X
gp Kapitel 5 Das Klassendesign (Teil 2)
  gp 5.1 Statische Klassenkomponenten
    gp 5.1.1 Die Realisierung eines Objektzählers
    gp 5.1.2 Zugriff auf statische Komponenten
    gp 5.1.3 Statische Klassenvariable in der Klasse »Circle«
    gp 5.1.4 Klassenspezifische Methoden
    gp 5.1.5 Statische Methoden in der Klasse »Circle«
    gp 5.1.6 Statische Klasseninitialisierer
    gp 5.1.7 Statische Klassen
    gp 5.1.8 Zusammenfassung
  gp 5.2 Delegate – Methodenzeiger unter .NET
    gp 5.2.1 Einführung in das Prinzip der Delegate
    gp 5.2.2 Zusammenfassung der Arbeitsschritte
    gp 5.2.3 Vereinfachter Aufruf eines Delegaten
    gp 5.2.4 Anonyme Methoden
  gp 5.3 Ereignisse eines Objekts
    gp 5.3.1 Ergänzung eines Ereignisses in einer Ereignisquelle
    gp 5.3.2 Die Behandlung eines Ereignisses im Ereignisempfänger
    gp 5.3.3 Wenn der Ereignisempfänger ein Ereignis nicht behandelt
    gp 5.3.4 Ereignisse mit Übergabeparameter
    gp 5.3.5 Zusammenfassung
  gp 5.4 Strukturen – eine Sonderform der Klassen
    gp 5.4.1 Die Definition einer Struktur
    gp 5.4.2 Initialisieren einer Struktur
    gp 5.4.3 Weitere Merkmale einer Struktur
    gp 5.4.4 Verschachtelung von Strukturen
    gp 5.4.5 Änderung der Klasse »Circle«
    gp 5.4.6 Zusammenfassung
  gp 5.5 Enumerationen (Aufzählungen)
    gp 5.5.1 Wertzuweisung an enum-Mitglieder
    gp 5.5.2 Alle Mitglieder einer Aufzählung durchlaufen
  gp 5.6 Referenz- und Wertetypen
    gp 5.6.1 Typumwandlung mit Boxing
    gp 5.6.2 Die Unboxing-Konvertierung
    gp 5.6.3 Zusammenfassung
  gp 5.7 Namensräume (Namespaces)
    gp 5.7.1 Zugriff auf Namespaces
    gp 5.7.2 Die »using«-Direktive
    gp 5.7.3 Vermeiden von Mehrdeutigkeiten
    gp 5.7.4 Aufrufe mit dem »::«-Operator umleiten
    gp 5.7.5 Namespaces festlegen
    gp 5.7.6 Zusammenfassung


Galileo Computing

5.4 Strukturen – eine Sonderform der Klassen  downtop

Bisher haben wir Objekte erzeugt, indem wir eine Klasse instanziierten. Objekte beanspruchen Systemressourcen, denn mit jedem Objekt ist ein Verwaltungsoverhead verbunden. Bei einzelnen Objekten fällt das kaum ins Gewicht; ein Objektarray, das möglicherweise Tausende typgleicher Objekte enthält, kann die Systemleistung jedoch spürbar negativ beeinflussen.

Die .NET-Laufzeitumgebung stellt aus diesem Grund ein Konstrukt bereit, das sich ähnlich wie eine Klasse verhält, dabei jedoch ressourcenschonend agiert, andererseits aber auch bestimmten Einschränkungen unterliegt: die Struktur.


Hinweis   Eine Struktur wird von der Laufzeitumgebung als Wertetyp behandelt. Im Abschnitt 5.6 werden wir uns mit dem Unterschied zwischen Referenz- und Wertetypen und den sich daraus ergebenen Konsequenzen noch ausgiebig beschäftigen.


Galileo Computing

5.4.1 Die Definition einer Struktur  downtop

Eine Struktur beschreibt einen Datentyp, der sich aus Elementen verschiedener Typen zusammensetzt, die miteinander in einer festen Beziehung stehen und somit eine in sich geschlossene Einheit bilden. Stellen Sie sich vor, Sie wollten ein Automobil beschreiben, das durch einen Preis, Höchstgeschwindigkeit und Hubraum gekennzeichnet ist. Außerdem soll das Auto die Methoden Fahren und Hupen veröffentlichen. Die Klassendefinition könnte folgendermaßen lauten:


public class Car {
  public decimal Preis; 
  public float Höchstgeschwindigkeit; 
  public float Hubraum;   
  public void Fahren() {
    // Anweisungen
  }   
  public void Hupen() {
    // Anweisungen
  } 
}

Tatsächlich unterscheidet sich die analoge Definition einer Struktur Car kaum von der einer Klasse – abgesehen vom Austausch des Schlüsselwortes class durch struct:


public struct Car {
  ...
}

Im ersten Moment mag das zu der Schlussfolgerung verleiten, eine Klasse grundsätzlich durch eine Struktur ersetzen zu können. Wie im weiteren Verlauf dieses Kapitels aber noch gezeigt wird, haben Strukturen im Vergleich zu den Klassen geringere Fähigkeiten.

Die Bestätigung der Aussage, eine Struktur sei ein Wertetyp, wird schon bei der Deklaration einer Strukturvariablen deutlich. Würde der Typ Car als Klasse definiert, müsste vor dem ersten Aufruf eine Instanz durch das Aufrufen des Operators new erzeugt werden:


// Car liegt als class-Definition vor
Car myCar = new Car();
myCar.Hubraum = 1900;

Eine Struktur wird demgegenüber von der Laufzeitumgebung jedoch wie die Variable eines elementaren Datentyps eingesetzt, da kein Verweis damit verknüpft ist:


// Car liegt als struct-Definition vor
Car myCar;

Der Zugriff auf die Elemente einer Struktur erfolgt ebenfalls mit dem Punktoperator, z.B.:


myCar.Hubraum = 1900;

Um eine Struktur zu definieren, bieten sich folgende Möglichkeiten an:

gp  eigenständig in einer separaten Quellcodedatei
gp  parallel neben anderen Klassen und Strukturen in einer Quellcodedatei
gp  im Deklarationsabschnitt einer Klasse

Die lokale Definition innerhalb einer Methode ist hingegen nicht erlaubt und führt zu einer Fehlermeldung.

Eigenständige Strukturdefinition

Eigenständige Strukturen, die nicht innerhalb einer Klasse definiert sind, können entweder public oder internal sein. Wird kein Zugriffsmodifizierer angegeben, gilt eine Struktur internal.

Bei der Festlegung eines Feldes vom Typ einer Struktur in einer Klasse ist dem Zugriffsmodifizierer besondere Beachtung zu schenken. Betrachten Sie dazu das folgende Codefragment in einer Anwendung vom Typ Klassenbibliothek:


internal struct Person {
  string Name;
  int Alter;
}
public class MyOwnClass {
  public Person pers;
}

Die Struktur Person ist internal definiert und beschränkt die Sichtbarkeit auf die aktuelle Assembly, in diesem Fall also auf die Klassenbibliothek. Die Klasse MyOwnClass ihrerseits ist public deklariert.

Der Code birgt in sich einen Widerspruch. In der öffentlichen Klasse ist das öffentliche Feld pers vom Typ Person enthalten. Die Sichtbarkeit des Typs Person ist per Definition jedoch auf die aktuelle Anwendung, also auf die Klassenbibliothek, begrenzt ist. Tatsächlich wird die Entwicklungsumgebung diesen Widerspruch auch sofort mit einer Syntaxfehlermeldung quittieren, da gegen die folgende Regel verstoßen wird:


Ist ein Feld vom Typ einer Struktur, kann das Feld nicht öffentlicher sein als die Definition der Struktur.

Aus dieser Regel lässt sich ableiten, dass eine öffentliche Struktur public, internal oder private deklarierte Variablen ihres Typs ermöglicht, eine internal-Struktur nur internal- und private-Datenmember.

Strukturdefinition in einer Klasse

Es sind kaum sinnvolle Anwendungsfälle für private deklarierte Strukturen innerhalb einer Klassendefinition vorstellbar. Wird in einer Klasse ein Feld privat deklariert, steht normalerweise immer das Bestreben dahinter, die gekapselte Variable über eine Eigenschaftsmethode öffentlich zugänglich zu machen. Der Rückgabewert der Eigenschaft muss aber dem Typ des Feldes entsprechen, was durch den eingeschränkten Gültigkeitsbereich einer privaten Struktur unmöglich gemacht wird.

Diesen Sachverhalt zeigt das folgende Codefragment. Die Struktur Person ist privat innerhalb der Klasse definiert. Ein privates Feld dieses Typs, pers, soll über die Eigenschaft PersDaten veröffentlicht werden. Da der Typ der Struktur allerdings außerhalb von ClassA unbekannt ist, kann die Eigenschaft nicht aufgelöst werden. Der C#-Compiler ist intelligent genug, diese Inkonsistenz festzustellen.


// --------- fehlerhafter Programmcode ------------ 
class ClassA {
  private struct Person {
    public string Name;
    public int Alter;
  }
  private Person pers;
  // in der Definition des Typs der Eigenschaft wird von der
  // Entwicklungsumgebung ein Widerspruch erkannt
  public Person PersDaten {
    get{}
    set{pers = value;}
  }
}


Galileo Computing

5.4.2 Initialisieren einer Struktur  downtop

Eine Struktur ist die Beschreibung eines Datentyps. Um das Objekt einer Struktur nutzen zu können, benötigt man eine Variable dieses Typs. Im folgenden Beispiel ist die Struktur Person mit den Feldern Name und Alter definiert. In der Main-Methode wird die Variable pers vom Typ Person deklariert, und den Feldern werden Werte zugewiesen.


struct Person {
  public string Name;
  public int Alter;
}
class ClassA {
  static void Main(string[] args) {
    Person pers;
    pers.Name = "Willi Jakob";
    pers.Alter = 35;  
  }
}

Der Name (»Willi Jakob«) und das Alter (35) sind einem ganz bestimmten Element zugeordnet, nämlich pers. Das erinnert an die Instanzvariablen einer Klasse, die ebenfalls objektgebunden sind. Der Vergleich ist auch nicht falsch, denn eine Strukturvariable ist einem Objektverweis sehr ähnlich, was durch die zweite Variante, eine Variable vom Typ einer Struktur zu deklarieren, besonders deutlich wird:


Person pers = new Person();
pers.Alter = 35;  

Die Syntax ist dieselbe wie bei der Instanziierung einer Klasse und deutet bereits darauf hin, dass es innerhalb einer Struktur einen Konstruktor geben muss, der parameterlos ist. Mit new wird dieser Konstruktor aufgerufen, der, wie auch der Konstruktor einer Klasse, die Felder des Objekts initialisiert.


Deklarieren Sie ein Objekt vom Typ einer Struktur mit new, sind alle Felder initialisiert und ermöglichen einen sofortigen Zugriff. Verzichten Sie auf den new-Operator, muss einem Feld zuerst ein Wert zugewiesen werden, bevor es ausgewertet werden kann.

Aus diesem Grund lehnt der C#-Compiler die Kompilierung des folgenden Codefragments ab: In der vierten Anweisung wird versucht, den Inhalt des noch uninitialisierten Feldes an der Konsole anzuzeigen.


Person pers1 = new Person();
Person pers2;
Console.Write(pers1.Name);
Console.Write(pers2.Name);


Galileo Computing

5.4.3 Weitere Merkmale einer Struktur  downtop

Klassen und Strukturen stellen in vielerlei Hinsicht dieselben Möglichkeiten zur Verfügung. Beispielsweise können in Strukturen Eigenschaften, Methoden und Ereignisse definiert sowie Ereignishandler und Schnittstellen implementiert werden. Die Methodenüberladung ist nach denselben Regeln wie in den Klassen erlaubt.

Es gibt einige Einschränkungen, die in Kauf genommen werden müssen, wenn Sie sich anstelle einer Klasse für eine Struktur entscheiden. Das wichtigste Unterscheidungsmerkmal ist, dass Strukturen weder die Eigenschaften und Methoden anderer Klassen oder Strukturen erben noch ihre eigenen Mitglieder anderen Klassen oder Strukturen vererben können. Von dieser Regel gibt es nur eine Ausnahme: Implizit werden Strukturen aus der Klasse ValueType abgeleitet, die ihrerseits direkt eine Subklasse von Object ist und deren Methoden in passender Weise überschreibt.

Standardmäßig stellt eine Struktur einen parameterlosen Konstruktor bereit, der mits


Person pers = new Person();

aufgerufen werden kann.

Strukturen lassen die Definition weiterer Konstruktoren zu, die jedoch parametrisiert sein müssen, denn das Überschreiben des parameterlosen Konstruktors einer Struktur ist nicht zulässig. Fügen Sie einen parametrisierten Konstruktor hinzu, müssen darin alle Felder der Struktur initialisiert werden, wie das folgende Beispiel des einparametrigen Konstruktors zeigt.


struct Person {
  public string Name;
  public int Alter;
  // Konstruktor
  public Person(string name) {
    Alter = 0;
    Name = name;
  }
}

Der Aufruf eines parametrisierten Konstruktors führt nur über den new-Operator. Aber Vorsicht ist hierbei geboten, denn das folgende Codefragment hat die doppelte Initialisierung der Variablen pers zur Folge, weil in der zweiten Anweisung ein parametrisierter Konstruktor aufgerufen wird:


Person pers;
pers = new Person("Willi", 13);

Läge dem Typ Person eine Klasse zugrunde, würde es nur zu einem Konstruktoraufruf kommen.

Es gibt noch ein anderes Merkmal, in dem sich Strukturen von Klassen unterscheiden. Während die Initialisierung eines Feldes in einer Klasse anstandslos möglich ist, wird das vom C#-Compiler in einer Struktur abgelehnt. Die folgende Definition von Person ist deshalb auch falsch:


public struct Person {
  // die Initialisierung der Felder ist unzulässig
  public string Zuname = "";
  public int PersonalID = 0;
}

Richtig muss es lauten:


public struct Person {
  public string Zuname;
  public int PersonalID;
}


Galileo Computing

5.4.4 Verschachtelung von Strukturen  downtop

Sollte es erforderlich sein, können Sie Stukturen auch ineinander verschachteln.


public struct Mitarbeiter {
  public struct Position {
    public string Bezeichnung;
    public double Gehalt;
  }
  public string Geschlecht;
  public int Alter;
  public Position Rang;
}

Die Struktur Position mit ihren öffentlichen Elementen Bezeichnung und Gehalt ist in die Struktur Mitarbeiter eingebettet, die ihrerseits selbst drei Elemente für den externen Zugriff bereitstellt. Das Element Rang ist vom Typ der eingebetteten Struktur.

Der Zugriff auf die untergeordnete Ebene erfolgt mit der üblichen Punktnotation. Zuerst wird das äußere Element genannt, danach das direkt untergeordnete, z.B.:


Mitarbeiter newPerson;
newPerson.Rang.Bezeichnung = "Boss";
newPerson.Rang.Gehalt = 20000; 


Galileo Computing

5.4.5 Änderung der Klasse »Circle«  downtop

Wir wollen uns nun erneut der Klasse Circle zuwenden. Grundsätzlich wäre es denkbar, die Klasse Circle in eine Strukturdefinition umzuschreiben. Da wir Circle jedoch später in eine Vererbungshierarchie zwingen, kommt diese Änderung nicht in Betracht, da Strukturen grundsätzlich nicht abgeleitet werden können. An einer anderen Stelle bietet es sich allerdings an, die aktuelle Implementierung durch eine Struktur zu ersetzen: Es handelt sich dabei um die beiden Mittelpunktkoordinaten xKoordinate und yKoordinate, die nun durch die Struktur Point beschrieben werden sollen.


Hinweis   In der .NET-Klassenbibliothek ist mit System.Drawing.Point eine sehr ähnliche Struktur bereits vordefiniert und würde sich gleichermaßen anbieten.

Point ist sehr einfach aufgebaut und hat nur die beiden Felder X und Y, die später den Mittelpunkt eines Kreisobjekts beschreiben sollen. Außerdem enthält die Struktur einen zweiparametrigen Konstruktor, dem beim Aufruf die Punktkoordinaten übergeben werden.


public struct Point {
  public int X;
  public int Y;
  public Point(int x, int y) {
    X = x;
    Y = y;
  }
}

In der Klasse Circle zieht unsere Absicht selbstverständlich Änderungen nach sich. Zunächst werden die beiden Felder xKoordinate und yKoordinate durch ein Feld vom Typ der Struktur Point ersetzt, also:


private Point center = new Point();

Dabei sollte explizit der parameterlose Konstrukor aufgerufen werden, damit X und Y im Feld center von Anfang an initialisiert sind und einen definierten Anfangszustand aufweisen.

Von der Einführung der Struktur Point ist auch der Konstruktor betroffen, der die beiden Mittelpunktkoordinaten in seinen Parametern erwartet. Diese werden den gleichnamigen Feldern des Circle-eigenen Feldes center vom Typ Point übergeben.


public Circle(double Radius, int X, int Y) : this(Radius) {
  this.center.X = X;
  this.center.Y = Y;
}

Die beiden Eigenschaften XKoordinate und YKoordinate sollen auch weiterhin die Möglichkeit bieten, ein Kreisobjekt horizontal oder vertikal zu verschieben bzw. die einzelnen Positionswerte zu ermitteln. Die Implementierung muss auch hier an das neue Feld center angepasst werden.


public int XKoordinate {
  get {return this.center.X;}
  set {this.center.X = value;}
}
public int YKoordinate {
  get {return this.center.Y;}
  set {this.center.Y = value;}
}

Eine gute Klassendefinition zeichnet sich nicht nur dadurch aus, die Implementierung auf das Notwendigste zu beschränken, sondern deckt auch die Fälle ab, die für einen Benutzer unter Umständen sinnvoll sein könnten. Sehen wir uns dazu an dieser Stelle noch einmal die beiden Eigenschaften XKoordinate und YKoordinate an. Soll der Mittelpunkt eines Kreisobjekts diagonal verschoben werden, sind zwei Anweisungen notwendig. Vorteilhafter ist es, dasselbe mit einer Anweisung zu erreichen. Die Verbesserung soll durch eine Methode erzielt werden, die wir MoveXY bezeichnen und ein Point-Objekt vom Aufrufer entgegennimmt:


public void MoveXY(Point newCenterPoint) {
  this.center = newCenterPoint;
}

Da eine Struktur ein Wertetyp ist, schreiben sich die Felder X und Y der im Parameter newCenterPoint übergebenen Koordinaten in die gleich lautenden Felder von center.

Sehen wir uns nun in einem Codefragment an, wie einfach es ist, diese Methode zu benutzen. Es wird dabei davon ausgegangen, dass ein konkretes Circle-Objekt namens kreis vorliegt. Mit


Point newPoint = new Point(150, 315);
kreis.MoveXY(newPoint);

übergeben wir der Methode ein Point-Objekt. Benötigen wir dieses Objekt zur Laufzeit der Anwendung nicht mehr, kann es auch in der Argumentliste erzeugt werden.


kreis.MoveXY(new Point(150, 315)); 

Zuletzt ergänzen wir die Klasse Circle noch um einen Konstruktor, der neben dem Radius eine Point-Referenz als Argument erwartet:


// Konstruktor
public Circle(double Radius, Point pt) : this(Radius) {
  this.center = pt;
}

Damit wird eine Instanziierung der Klasse mit


Circle kreis = new Circle(2, new Point(5, 12));

möglich.

Den Code des Beispiels CircleApplication mit allen Änderungen, die wir bisher in diesem Kapitel vorgenommen haben, finden Sie auf der Buch-CD unter:

...\Kapitel 5\CircleApplication_3


Galileo Computing

5.4.6 Zusammenfassung  toptop

gp  Eine Struktur ist ein Wertetyp und hat einen geringeren Verwaltungsoverhead als ein auf einer Klasse basierendes Objekt.
gp  Strukturen ähneln den Klassen, werden aber durch das Schlüsselwort struct definiert. Die Sichtbarkeit wird durch Zugriffsmodifizierer beschrieben. Fehlt dieser, gilt eine Struktur per Vorgabe als internal.
gp  Strukturen können sowohl als eigenständige Komponenten als auch innerhalb einer Klasse definiert werden. Sie können Eigenschaften, Methoden und Ereignisse definieren sowie Schnittstellen implementieren. Strukturen und Klassen sind sich sehr ähnlich, allerdings sind Strukturen nicht ableitbar.
gp  Strukturen gliedern sich wie alle anderen Typen in die .NET-Klassenhierarchie ein. Grundsätzlich werden alle Strukturen implizit aus der Klasse ValueType abgeleitet.
gp  Eine Strukturvariable kann wie ein elementarer Datentyp ohne new deklariert werden. Dabei werden jedoch die internen Felder nicht initialisiert. Wird mit new instanziiert, weisen die Felder typabhängige Standardinitialisierungswerte auf.
gp  Strukturen haben Konstruktoren. Wird ein parametrisierter Konstruktor aufgerufen, muss in jedem Fall die Objektvariable mit dem new-Operator initialisiert werden.
gp  In einem explizit definierten Konstruktor müssen alle Strukturmitglieder initialisiert werden, also auch jene, denen über die Parameterliste kein Wert zugewiesen wird.
gp  Der parameterlose Konstruktor einer Struktur darf nicht überschrieben werden.
 << zurück
  
  Zum Katalog
Zum Katalog: Visual C# 2005
Visual C# 2005
bestellen
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchtipps
Zum Katalog: Fortgeschrittene Programmierung mit Visual C# 2005






 Fortgeschrittene
 Programmierung
 mit Visual C# 2005


Zum Katalog: Einstieg in Visual C# 2005






 Einstieg in
 Visual C# 2005


Zum Katalog: Einstieg in Visual Basic 2005






 Einstieg in
 Visual Basic 2005


Zum Katalog: Visual Basic 2005






 Visual Basic 2005


Zum Katalog: Java ist auch eine Insel






 Java ist auch eine
 Insel


Zum Katalog: Konzepte und Lösungen für Microsoft-Netzwerke






 Konzepte und
 Lösungen für
 Microsoft-Netzwerke


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo








Copyright © Galileo Press 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de